package com.rongji.dfish.misc.docpreview.builder;

import com.rongji.dfish.base.util.Utils;
import com.rongji.dfish.misc.docpreview.BuilderConfig;
import com.rongji.dfish.misc.docpreview.data.*;
import org.apache.poi.hwpf.converter.PicturesManager;
import org.apache.poi.hwpf.converter.WordToHtmlConverter;
import org.apache.poi.hwpf.usermodel.PictureType;
import org.apache.poi.xwpf.converter.core.FileImageExtractor;
import org.apache.poi.xwpf.converter.core.FileURIResolver;
import org.apache.poi.xwpf.converter.xhtml.XHTMLConverter;
import org.apache.poi.xwpf.converter.xhtml.XHTMLOptions;
import org.apache.poi.xwpf.usermodel.XWPFDocument;

import javax.xml.parsers.DocumentBuilderFactory;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

/**
 * 转成HTML
 */
public class HtmlBuilder {
    BuilderConfig config;

    /**
     * 构造函数
     * @param config 配置
     */
    public HtmlBuilder(BuilderConfig config){
        this.config=config;
    }
    /**
     * 构建 text
     * @param doc 文档
     * @return String
     */
    public String build(Document doc) {
        if(doc.originDoc(null) instanceof XWPFDocument){
            ByteArrayOutputStream out=new ByteArrayOutputStream();

            XWPFDocument document =(XWPFDocument)doc.originDoc(null);
            XHTMLOptions options = XHTMLOptions.create().indent(4);
            // 导出图片
            final String datePath ;
            synchronized (SDF){
                datePath=SDF.format(new Date());
            }
            String contextId=getId();


            File imageFolder = new File(config.getFileRootPath()+"/"+datePath+"/"+contextId);
            options.setExtractor(new FileImageExtractor(imageFolder));
            // URI resolver
            options.URIResolver(url->
                    config.getImageURLConverter().getDownloadURL(datePath+"/"+contextId+"/"+url));

            try {
                XHTMLConverter.getInstance().convert(document, out, options);
                return new String(out.toByteArray(),"UTF-8");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        StringBuilder sb=new StringBuilder();
        build(doc,sb);
        return sb.toString();
    }
    protected void build(Document doc, StringBuilder sb) {
        sb.append("<div class='document_prew'>");
        for(DocumentElement de:doc.getBody()){
            if(de instanceof Paragraph){
                build((Paragraph)de, sb);
            }else if(de instanceof Table){
                build((Table)de, sb);
            }else if(de instanceof PageBreaker){
                build((PageBreaker)de, sb);
            }
        }
        sb.append("</div>");
    }
    protected void build(PageBreaker b, StringBuilder sb) {
        sb.append("<hr/>");
    }

    protected void build(Paragraph p, StringBuilder sb) {
        if((p.getAlignment()!=null&&!"LEFT".equals(p.getAlignment()))||p.getIndentation()!=null){
            sb.append("<p style=\"");
            if(p.getAlignment()!=null&&!"LEFT".equals(p.getAlignment())) {
                sb.append("text-align:")
                        .append(p.getAlignment()).append(';');
            }
            if(p.getIndentation()!=null &&p.getIndentation()!=0) {
                sb.append("text-indent:")
                        .append(p.getIndentation()/15)
                        .append("px;");//twenties 转为px
            }
            sb.append("\">");
        }else{
            sb.append("<p>");
        }
        for(ParagraphElement pe:p.getBody()){
            if(pe instanceof CharacterRun){
                build((CharacterRun)pe, sb);
            }else if(pe instanceof Drawing){
                build((Drawing)pe, sb);
            }
        }
        sb.append("</p>\r\n");
    }
    protected void build(CharacterRun cr, StringBuilder sb) {
        sb.append("<span");
        if(cr.getColor() != null||cr.getFontFamily() != null||
                cr.getFontSize() != null||cr.getStrikeType() != null||
                cr.isBold()||cr.isItalic()) {
            sb.append(" style=\"");
            if (cr.getColor() != null) {
                sb.append("color:");
                sb.append(cr.getColor());
                sb.append(';');
            }
            if (cr.getFontFamily() != null) {
                sb.append("font-family:");
                sb.append(toHtmlFontFamily(cr.getFontFamily()));
                sb.append(';');
            }
            if (cr.getFontSize() != null) {
                sb.append("font-size:");
                sb.append(cr.getFontSize());
                sb.append("pt;");
            }
            if (cr.isBold()) {
                sb.append("font-weight:bold;");
            }
            if (cr.isItalic()) {
                sb.append("font-style: italic;");
            }
            if (cr.getStrikeType() != null) {
                String stikeCss = "none";
                switch (cr.getStrikeType()) {
                    case CharacterRun.STRIKE_LINE_THROUGH:
                        stikeCss = "line-through";
                        break;
                    case CharacterRun.STRIKE_OVERLINE:
                        stikeCss = "overline";
                        break;
                    case CharacterRun.STRIKE_UNDERLINE:
                        stikeCss = "underline";
                        break;
                    default:
                        stikeCss = "line-through";
                }
                sb.append("text-decoration:");
                sb.append(stikeCss);
                sb.append(';');
            }
            sb.append('"');
        }
        sb.append('>');
        Utils.escapeXMLword(cr.getText(), sb );
        sb.append("</span>");
    }
    protected void build(Drawing cr, StringBuilder sb) {
        // 相对路径 config  业务可以扩展此方法改写路径
        // 如果地址是动态地址如 xxx/download?id=xxx&seq=1
        // Config中原则上要提供该参数会回调方法。
        // 如果地址是可访问文件地址如 docpic/datefolder/id/seq.jpg
        sb.append("<img");
        if(cr.getPicHeight()!=null){
            sb.append(" height='").append(cr.getPicHeight()).append("'");
        }
        if(cr.getPicWidth()!=null){
            sb.append(" width='").append(cr.getPicWidth()).append("'");
        }
        sb.append(" src='");
        sb.append(this .config.getImageURLConverter().getDownloadURL(cr.getPicPath()));
        sb.append("'/>");
    }

    protected void build(Table table, StringBuilder sb) {
        //FIXME 各列的宽度
        sb.append("<table>");
        for(TableRow row:table.getRows()){
            build(row,sb);
        }
        sb.append("</table>\r\n");
    }
    protected void build(TableRow row, StringBuilder sb) {
        sb.append("<tr>");
        for(TableCell cell:row.getCells()){
            build(cell,sb);
        }
        sb.append("</tr>");
    }
    protected void build(TableCell td, StringBuilder sb) {
        sb.append("<td");
        if(td.getColSpan()!=null&&td.getColSpan()>1){
            sb.append(" colspan='");
            sb.append(td.getColSpan());
            sb.append('\'');
        }
        if(td.getRowSpan()!=null&&td.getRowSpan()>1){
            sb.append(" rowspan='");
            sb.append(td.getRowSpan());
            sb.append('\'');
        }
        sb.append(">");
        for(DocumentElement de:td.getBody()){
            if(de instanceof Paragraph){
                build((Paragraph)de, sb);
            }else if(de instanceof Table){
                build((Table)de, sb);
            }
        }
        sb.append("</td>");
    }

    private static String toHtmlFontFamily(String font){
        String r=FONT_FAMILY.get(font);
        return r==null?font:r;
    }
    private static final Map<String,String> FONT_FAMILY;
    static{
        FONT_FAMILY=new HashMap<>();
        FONT_FAMILY.put("仿宋","FangSong");
        FONT_FAMILY.put("仿宋_GB2312","FangSong");
        FONT_FAMILY.put("华文仿宋","FangSong");

        FONT_FAMILY.put("宋体","SimSun");
        FONT_FAMILY.put("新宋体","SimSun");
        FONT_FAMILY.put("华文宋体","SimSun");
        FONT_FAMILY.put("宋体_GB2312","SimSun");
        FONT_FAMILY.put("华文中宋","SimSun");
        FONT_FAMILY.put("宋体(标题)","SimSun");
        FONT_FAMILY.put("宋体(正文)","SimSun");

        FONT_FAMILY.put("黑体","SimHei");
        FONT_FAMILY.put("华文黑体","SimHei");

        FONT_FAMILY.put("楷体","KaiTi");
        FONT_FAMILY.put("华文楷体","KaiTi");
        FONT_FAMILY.put("楷体_GB2312","KaiTi");

        FONT_FAMILY.put("微软雅黑","\"Microsoft YaHei\",微软雅黑");


    }


    Random r=new Random();
    String id;

    /**
     * 产生有一定顺序的ID。不完全保证ID不重复
     * 某些月份可能是现有 EF开头的ID 后有0123
     * 1.024秒内的ID。前面5位是一样的。后面3位为随机
     * @return ID String
     */
    public synchronized String getId() {
        if(id==null){
            long now=System.currentTimeMillis();
            char[] cs = new char[8];
            cs[0] = ALPHABET[(int)(now>>30 &0x1F)];
            cs[1] = ALPHABET[(int)(now>>25 &0x1F)];
            cs[2] = ALPHABET[(int)(now>>20 &0x1F)];
            cs[3] = ALPHABET[(int)(now>>15 &0x1F)];
            cs[4] = ALPHABET[(int)(now>>10 &0x1F)];
            cs[5] = ALPHABET[r.nextInt(32)];
            cs[6] = ALPHABET[r.nextInt(32)];
            cs[7] = ALPHABET[r.nextInt(32)];
            id=new String(cs);
        }
        return id;
    }
    private static final char[] ALPHABET = {
            '0', '1', '2', '3', '4', '5', '6', '7',
            '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
            'G', 'H', 'J', 'K', 'M', 'N', 'P', 'Q',
            'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z'
    };
    private static final SimpleDateFormat SDF=new SimpleDateFormat("yyMM");
}
